package edu.uoc.vmerinom.tfc.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.Transactional;
import org.jboss.seam.log.Log;

import edu.uoc.vmerinom.tfc.domain.Asistente;
import edu.uoc.vmerinom.tfc.domain.Grupo;
import edu.uoc.vmerinom.tfc.domain.Reunion;
import edu.uoc.vmerinom.tfc.domain.Usuario;

/**
 * Clase de servicios con las operaciones de acceso a base de datos para el
 * módulo de reuniones.
 * 
 * @author Víctor Manuel Merino Martínez
 * 
 */
@Name("reunionesService")
@Scope(ScopeType.CONVERSATION)
public class ReunionesService {
	/**
	 * Gestor de entidades.
	 */
	@In(create = true)
	EntityManager entityManager;

	/**
	 * Log para la trazabilidad de errores.
	 */
	@Logger
	Log log;

	/**
	 * Devuelve un listado de reuniones cuya fecha de baja esté vacía o sea
	 * superior a la fecha de hoy. El listado estará ordenado de forma
	 * descendente por fecha de reunión.
	 * 
	 * @return Listado de reuniones activas
	 */
	@SuppressWarnings("unchecked")
	public List<Reunion> obtenerReunionesActivas() {
		if (log.isDebugEnabled()) {
			log.debug("ReunionesService.obtenerReunionesActivas()");
		}
		String query = "from Reunion r where r.baja is null or r.baja > :currentDate order by r.fecha desc";
		log.info(query);
		return entityManager.createQuery(query).setParameter("currentDate", new Date()).getResultList();
	}

	/**
	 * Devuelve un listado de reuniones ordenados de forma descendente por fecha
	 * de reunión.
	 * 
	 * @return Listado de reuniones
	 */
	@SuppressWarnings("unchecked")
	public List<Reunion> obtenerReuniones() {
		if (log.isDebugEnabled()) {
			log.debug("ReunionesService.obtenerReuniones()");
		}
		String query = "from Reunion r order by r.fecha desc";
		log.info(query);
		return entityManager.createQuery(query).getResultList();
	}

	/**
	 * Devuelve un listado con todos los usuarios de BD activos a la fecha que
	 * recibe por parámetros.
	 * 
	 * @param fechaReunion
	 *            Fecha de la reunión
	 * @return Listado de usuarios
	 */
	@SuppressWarnings("unchecked")
	public List<Usuario> obtenerUsuarios(Date fechaReunion) {
		if (log.isDebugEnabled()) {
			log.debug("ReunionesService.obtenerUsuarios(fechaReunion:" + fechaReunion + ")");
		}
		final StringBuffer queryString = new StringBuffer("from Usuario u where (u.baja is null or u.baja > :fechaReunion)");
		queryString.append(" and u.perfil.administrador = false order by u.nombre, u.primerApellido, u.segundoApellido");
		log.info(queryString);
		Query query = entityManager.createQuery(queryString.toString()).setParameter("fechaReunion", fechaReunion);
		return query.getResultList();
	}

	/**
	 * Devuelve un listado de los grupos (ordenados por nombre) que están
	 * representandos por los asistentes en la reunión que recibe por
	 * parámetros.
	 * 
	 * @param reunion
	 *            Reunión
	 * @return Listado de grupos representados
	 */
	@SuppressWarnings("unchecked")
	public List<Grupo> obtenerGruposRepresentados(Reunion reunion) {
		if (log.isDebugEnabled()) {
			log.debug("ReunionesService.obtenerGruposRepresentados(reunion:" + reunion + ")");
		}
		List<Grupo> grupos = null;
		if (reunion.getAsistentes() != null && !reunion.getAsistentes().isEmpty()) {
			List<Usuario> usuarios = new ArrayList<Usuario>();
			for (Asistente asistente : reunion.getAsistentes()) {
				usuarios.add(asistente.getUsuario());
			}
			StringBuffer qs = new StringBuffer("select distinct c.grupo from Componente c where c.usuario in (:usuarios) ");
			qs.append("and (c.grupo.baja is null or c.grupo.baja > :fechaReunion) order by c.grupo.nombre");
			log.info(qs);
			Query query = entityManager.createQuery(qs.toString()).setParameter("usuarios", usuarios).setParameter("fechaReunion", reunion.getFecha());
			grupos = query.getResultList();
		} else {
			grupos = new ArrayList<Grupo>();
		}
		return grupos;
	}

	/**
	 * Actualiza el estado de la reunión con los datos actuales de base de
	 * datos.
	 * 
	 * @param reunion
	 *            Reunión a actualizar
	 */
	public void actualizarEstadoReunion(Reunion reunion) {
		if (log.isDebugEnabled()) {
			log.debug("ReunionesService.actualizarEstadoReunion(reunion:" + reunion + ")");
		}
		if (reunion != null && reunion.getId() != null) {
			entityManager.refresh(reunion);
			if (reunion.getAsistentes() != null && !reunion.getAsistentes().isEmpty()) {
				for (Asistente asistente : reunion.getAsistentes()) {
					entityManager.refresh(asistente);
				}
			}
		}
	}

	/**
	 * Inserta o modifica los datos de la reunión que recibe por parámetros. Si
	 * recibe los listados de asistentes nuevos y eliminados los
	 * insertará/eliminará en/de la reunión.
	 * 
	 * @param reunion
	 *            Reunión a insertar/modificar
	 * @param nuevos
	 *            Lista de asistentes nuevos en la reunión
	 * @param eliminados
	 *            Lista de asistentes eliminados de la reunión
	 */
	@Transactional
	public void insertarModificarReunion(Reunion reunion, List<Usuario> nuevos, List<Asistente> eliminados) {
		if (log.isDebugEnabled()) {
			log.debug("ReunionesService.insertarModificarReunion(reunion:" + reunion + ", nuevos:" + nuevos + ", eliminados:" + eliminados + ")");
		}
		if (reunion != null) {
			final Date hoy = new Date(System.currentTimeMillis());
			if (reunion.getId() == null) {
				reunion.setCreado(hoy);
			}
			reunion.setModificado(hoy);
			entityManager.persist(reunion);
			if (eliminados != null && !eliminados.isEmpty()) {
				if (log.isDebugEnabled()) {
					log.debug("Eliminamos los asistentes...");
				}
				for (Asistente asistente : eliminados) {
					entityManager.remove(asistente);
				}
			}
			if (nuevos != null && !nuevos.isEmpty()) {
				if (log.isDebugEnabled()) {
					log.debug("Insertamos los asistentes...");
				}
				for (Usuario usuario : nuevos) {
					Asistente asistente = new Asistente(reunion, usuario, hoy, hoy);
					entityManager.persist(asistente);
				}
			}
		}
		entityManager.flush();
	}
}
